#include <ProcessComposition.h>
#include <Scheduler.h>
#include <csp_protocol_msg.h>
#include <csp_debug.h>
#include <pthread.h>
#include <queue>
#include <ReadyQ.h>

Scheduler *
Scheduler::sinstance=NULL;

Scheduler *
Scheduler::Instance(){
	if(NULL == sinstance){
		sinstance = new Scheduler();
	}
	return sinstance;
}

Scheduler::Scheduler(){
	pid_counter = 100;
	if(pthread_mutex_init(&sched_mutex, NULL)){
		perror("Could not initiate mutex in scheduler.");
		exit(-1);
	}
	live_processes = 0;
	stop_when_empty = 1;
	/*
	   int i;
	   for(i=0; i < 1000; i++){
	   check[i] = false;
	   }
	   */
	memset(lastRun, 0, sizeof(ProcessComposition*)*MAX_SPU_SLOTS*NUMTHREADS);

}


ProcessComposition *
Scheduler::get(unsigned char slot, int spe_id){
	ProcessComposition *p=NULL, *tmp=NULL;
	bool run_twice;
	pthread_mutex_lock(&sched_mutex);

	//	if( ready_q.Size(slot) ){
	//		p = ready_q.Get(slot);
	//	}else if( init_q.size() ){
	//
	//		p = &init_q.pop();
	//		p->Setup(slot, pid_counter++);
	//
	//	}

	//if( init_q.size() ){

	//	p = &init_q.pop();
	//	p->Setup(slot, pid_counter++);

	//}else 
	
	if( ready_q.Size(slot) ){

		p = ready_q.Get(slot);

		/* To prevent a deadlock a process must not execute two times in 
		 * a row on the same SPE. If the situation arises the process
		 * gets re-scheduled until another SPE picks up the process. 
		 * */
		/*
		   tmp = p;

		   run_twice = (spe_id == p->GetLastSpeID() && p ==  lastRun[spe_id-1][slot]);

		   if( ready_q.Size(slot) && run_twice ){
		   p = ready_q.Get(slot);
		   ready_q.Push(*tmp);
		   }else if( run_twice ){
		   p = NULL;
		   ready_q.Push(*tmp);
		   }
		   */
	}
	/*
	   if(p){
//check[p->GetPID()] = false; // TODO: remove
lastRun[spe_id-1][slot] = p;
}
*/
pthread_mutex_unlock(&sched_mutex);
return p;
}


void
Scheduler::PutProcess(ProcessComposition &p, int state){
	bool triggerEvent = false;
	int exitStatus = 0;
	pthread_mutex_lock(&sched_mutex);


	ASSERT( STATE_EXIT == state 
			|| STATE_INIT ==  state 
			|| STATE_YIELD == state );
	//if( !( STATE_EXIT ==  state 
	//			|| STATE_INIT ==  state 
	//			|| STATE_YIELD == state )){
	//	p.Print();
	//}


	//StopWhenEmpty();	
	if( -1 == p.GetPID() ){
		live_processes++;
		//init_q.push(p);	
		p.Setup(0, pid_counter++);
	}
	
	if( STATE_EXIT == state ){
		//DEBUG_PRINTF("***** PID=%i exited\n", p.GetPID());
		live_processes--;
		triggerEvent = true; /* Notice. */
		exitStatus = p.GetExitStatus();
	}else{
		/*		
				ASSERT( !check[p.GetPID()] );

				if( check[p.GetPID()] ){
				printf("lene\n");	
				}


				check[p.GetPID()] = true;
				*/
		ready_q.Push(p);	
	}
	pthread_mutex_unlock(&sched_mutex);

	/* Events can operate on the scheduler so it's important that we release
	 * any locks! Make sure that we're not touching any shared data at this point. */
	if( triggerEvent ){
		p.TriggerEvent(exitStatus);
	}
}

int
Scheduler::LiveProcesses(){
	int ret;
	pthread_mutex_lock(&sched_mutex);
	ret = (live_processes || stop_when_empty);
	pthread_mutex_unlock(&sched_mutex);
	return ret;
}


void
Scheduler::Put(vector<ProcessComposition*> &procs){
	pthread_mutex_lock(&sched_mutex);
	ProcessComposition *p;
	for(unsigned int i = 0; i < procs.size(); i++){
		ASSERT( STATE_INIT == procs[i]->GetState() || STATE_YIELD == procs[i]->GetState() || STATE_EXIT == procs[i]->GetState() );
		//if( !(STATE_INIT == procs[i]->GetState() || STATE_YIELD == procs[i]->GetState() || STATE_EXIT == procs[i]->GetState()) ){
		//	printf("Scheduler-put: ");
		//	procs[i]->Print();
		//}
		/*
		   procs[i]->SetLast(-1, -1);
		   */
		//PutProcess(*procs[i], procs[i]->GetState());
		p = procs[i];
		if( -1 == p->GetPID() ){
			live_processes++;
			//init_q.push(p);	
			p->Setup(0, pid_counter++);
		}
		ready_q.Push(*p);	
	}
	pthread_mutex_unlock(&sched_mutex);
}

void
Scheduler::put(ProcessComposition &p, int state){
	//bool test = 
	//	( STATE_INIT  == state || 
	//	  STATE_YIELD == state || 
	//	  STATE_EXIT  == state );
	//ASSERT( test );
	//if( !test ){
	//	printf("Scheduler-put: ");
	//	p.Print();
	//}


	PutProcess(p, state);
}
